package com.unis.common.secure.realm;

import com.unis.common.constant.Constants;
import com.unis.common.secure.authc.AccountLoginToken;
import com.unis.common.secure.authc.UserInfo;
import com.unis.common.util.CryptoUtil;
import com.unis.model.system.Account;
import com.unis.model.system.Permission;
import com.unis.model.system.Role;
import com.unis.service.system.AccountService;
import com.unis.service.system.DepartmentService;
import com.unis.service.system.PermissionService;
import com.unis.service.system.RoleService;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * shiro mybatis体系 相关授权验证
 * Created by Administrator on 2018/12/28/028.
 */
public class MybatisRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory.getLogger(MybatisRealm.class);

    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private DepartmentService departmentService;
    /*
     * Authorization 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
        // 因为非正常退出，即没有显式调用 SecurityUtils.getSubject().logout()
        // (可能是关闭浏览器，或超时)，但此时缓存依旧存在(principals)，所以会自己跑到授权方法里。
        if (!SecurityUtils.getSubject().isAuthenticated()) {
            this.clearCached();
            doClearCache(principals);
            SecurityUtils.getSubject().logout();
            return null;
        }
        UserInfo userInfo = (UserInfo)principals.getPrimaryPrincipal();
        //UserInfo userInfo = (UserInfo) principals.fromRealm(getName()).iterator().next();

        SimpleAuthorizationInfo info = null;
        Set<String> roles = new HashSet<String>();
        Set<String> permissions = new HashSet<String>();
        /*if (StringUtils.isNotBlank(userInfo.getIsAdmin()) && StringUtils.equalsIgnoreCase("1",userInfo.getIsAdmin())) {
            roles.add(Constants.SYSTEM_ADMIN_FLAG);
        }*/
        try {
            List<Role> roleList = roleService.queryCurrentUserRole(userInfo.getPk());
            if (roleList!=null && roleList.size()>0){
                info = new SimpleAuthorizationInfo();
                for (Role role : roleList){
                    roles.add(role.getCode());
                }
                info.addRoles(roles);
            }
            List<Permission> permissionList = permissionService.queryCurrentUserPermit(userInfo.getPk());
            if(permissionList!=null && permissionList.size()>0){
                for (Permission permission : permissionList){
                    permissions.add(permission.getCode());
                }
                info.addStringPermissions(permissions);
            }
        } catch (Exception e) {
            logger.error("realm doGetAuthorizationInfo get role or permission  is error");
            e.printStackTrace();
        }

        return info;
    }

    /**
     * 认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UserInfo userInfo = null;
        // 存储用户密码令牌
        boolean pkibz = false;
        try {
            AccountLoginToken authcToken = (AccountLoginToken) token;
            userInfo = authcToken.getUserInfo();
            if (StringUtils.isNotBlank(userInfo.getIdno())){
                pkibz = true;
            }
        } catch (Exception e) {
            userInfo = new UserInfo();
            userInfo.setIdno(((UsernamePasswordToken) token).getUsername());
        }
        SimpleAuthenticationInfo info = null;

        try {
            Account account = accountService.queryAccountByParm(userInfo.getPk(),userInfo.getUsername(),userInfo.getIdno(),userInfo.getJh());

            if(account!=null ){
                if (account.getOuttime()!=null && account.getOuttime().getTime()<new Date().getTime()){
                    throw new UnknownAccountException("用户已过期 [" + ((UsernamePasswordToken) token).getUsername() + "]");
                }
                userInfo = new UserInfo(account);
                /*if (StringUtils.isNotBlank(account.getDeptid())){
                    userInfo.setDept(departmentService.queryDepartmentByPrimaryKey(account.getDeptid()));
                }*/
            }

            if (userInfo==null){
                throw new UnknownAccountException("未发现用户 [" + ((UsernamePasswordToken) token).getUsername() + "]");
            }
            String password = "";
            if(pkibz){
                String sfzh = userInfo.getIdno().trim();
                password = CryptoUtil.encodePassphrase(sfzh,"1");
                info = new SimpleAuthenticationInfo(userInfo, password.toCharArray(), getName());
                info.setCredentialsSalt(ByteSource.Util.bytes( "Supe5Str0ngPassw0rdSalt:" + "1"));
            }else{
                password = userInfo.getPassword();
                info = new SimpleAuthenticationInfo(userInfo, password.toCharArray(), getName());
                String salt = userInfo.getSalt();
                if (salt != null) {
                    info.setCredentialsSalt(ByteSource.Util.bytes( "Supe5Str0ngPassw0rdSalt:" + salt));
                }
            }
        } catch (Exception e) {
            throw new AuthenticationException(e.getMessage(), e);
        }

        return info;
    }

    public void clearCached(){
        PrincipalCollection principals=SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }
}
